%{
/*
 * parser.y:
 * Clunix, cezanne@clunix.com, 2005.2
 *
 * Copyright (c) 2005 by Clunix Inc. All Rights Reserved.
 */

#include "config.h"

#ifdef ECM_DEBUG
#define YYDEBUG 1
#else
#define YYDEBUG 0
#endif

#define YYSIZE_T	ecm_size_t

#include <stdio.h>

#include "edl.h"

#define YYMALLOC	ecm_malloc
#define YYFREE		ecm_free

typedef void *yyscan_t;

union YYSTYPE;
struct YYLTYPE;

ECM_INLINE int edllex(union YYSTYPE *, struct YYLTYPE *, yyscan_t scanner);

static void
edlerror(struct YYLTYPE *dummy, yyscan_t scanner, sr_t **psr, ECMBOOL checkmode, ecm_uint32 *perrlineno, char **perrmsg, char *msg)
{
}

static void
add_lineno(sr_t *sr, ecm_uint32 lineno)
{
	sr_t	*sr_lineno;

	sr_lineno = sr_getc(sr, "lineno");
	if (sr_lineno == NULL)
		sr_createc_int(sr, "lineno", ST_INT, lineno);
	else
		sr_set_int(sr_lineno, lineno);
}

%}

%pure_parser

%name-prefix="edl"

%parse-param { yyscan_t scanner }
%parse-param { sr_t **psr }
%parse-param { ECMBOOL checkmode }
%parse-param { ecm_uint32 *perrlineno }
%parse-param { char **perrmsg }

%lex-param { yyscan_t scanner }

%union {
	int	i_value;
	char	*s_value;
	sr_t	*sr;
	ECMBOOL	bool;
       }

%start EDexpression

%token <s_value> String Identifier
%token <s_value> UIVar UIVars ASRT
%token <i_value> ArgVar MemberVar
%token <i_value> ENDOFBUF At AtAny Dollar Esterik
%token <i_value> LeftBrace RightBrace LeftAngle RightAngle
%token <i_value> While Switch CaseExec CaseVar
%token <i_value> LeftRound RightRound
%token <i_value> GreaterEqual LessEqual
%token <i_value> Plus Minus Equal NotEqual Comma Sharp Ampersand
%token <i_value> Same NotSame XeCreateName VectorPlus VectorMinus
%token <i_value> Question QuestionAny Colon Semicolon Assignment
%token <i_value> LogicalAnd LogicalOr LogicalNot Actuator SubEntity
%token <i_value> BitwiseOr
%token <s_value> UIArgVar CastSrt LNPath PassiveLNPath
%token <sr>	 CtxValVar
%token <i_value> CallFunc CallOnewayFunc

/* Types of all other things. */

%type <sr> EDexpression Expression expression
%type <sr> seq_expression expressions
%type <sr> ASRTV_expression
%type <sr> cast_expression set_expression
%type <sr> call_expression
%type <sr> xecreate_expression xedelete_expression
%type <sr> subentity_expression
%type <sr> vector_expression vector1_expression
%type <sr> CNP_expression CNP1_expression cond_expression
%type <sr> case_expression case_body case_exec_expression casevar_expression
%type <sr> while_expression
%type <sr> LNP_expression MemberLNP_expression
%type <sr> ArgVar_expression UIArg_expression UIVar_expression
%type <sr> CtxValVar_expression
%type <sr> func_expression arglist arguments
%type <sr> operators binary_operators unary_operators
%type <s_value> literal alpha_numeric
%type <sr> lnpath optional_lnpath
%type <sr> ASRTV asrt ASRV ASR ASR_stmt ASR_stmts ASR_block optional_ASRV
%type <bool> optional_ref
%type <s_value> LNP

%left Comma LeftRound RightRound
%left Semicolon
%left Actuator
%right Assignment
%right Question QuestionAny Colon
%right At AtAny While
%nonassoc CaseExec
%right LogicalAnd LogicalOr
%right BitwiseOr
%left XeCreateName
%nonassoc Equal NotEqual LeftAngle RightAngle GreaterEqual LessEqual Same NotSame
%left Plus Minus VectorPlus VectorMinus
%left LogicalNot Sharp CastSrt
%left SubEntity

%destructor { ecm_free($$); } Identifier
%destructor { sr_free($$); } expression Expression
%destructor { ecm_free($$); } ASRT
%%

EDexpression	: Expression ENDOFBUF
		{
			*(sr_t **)psr = $1;
			YYACCEPT;
		}
		| Expression error
		{
			if (ecm_empty_str(*perrmsg)) {
				*perrlineno = @2.first_line;
				ecm_asprintf(perrmsg, "syntax error");
			}
			sr_free($1);
			YYABORT;
		}
		;

Expression	: seq_expression | expression;

seq_expression	: expressions
		{
			$$ = build_seq_edi($1);
		}
		;

ASRTV_expression: ASRTV
		{
			$$ = buildv_edi(EDI_ASRTV, 1, $1);
		}
		;

vector_expression	: expression At expression
		{
			$$ = buildv_edi(EDI_VECTOR, 2, $3, $1);
		}
		;

vector1_expression	: expression AtAny expression
		{
			$$ = buildv_edi(EDI_VECTOR1, 2, $3, $1);
		}
		;

while_expression	: expression While expression
		{
			$$ = buildv_edi(EDI_WHILE, 2, $3, $1);
		}
		;

CNP_expression	: expression Question expression
		{
			$$ = buildv_edi(EDI_CNP, 2, $1, $3);
		}
		;

CNP1_expression	: expression QuestionAny expression
		{
			$$ = buildv_edi(EDI_CNP1, 2, $1, $3);
		}
		;

set_expression	: expression Assignment expression
		{
			$$ = buildv_edi(EDI_SET, 2, $1, $3);
		}
		;

xecreate_expression	: Plus expression
		{
			$$ = buildv_edi(EDI_XECREATE, 1, $2);
		}
		| expression XeCreateName expression
		{
			$$ = buildv_edi(EDI_XECREATE, 2, $1, $3);
		}
		| Plus asrt
		{
			$$ = buildv_edi(EDI_XECREATE_SA, 1, $2);
		}
		| asrt XeCreateName expression
		{
			$$ = buildv_edi(EDI_XECREATE_SA, 2, $1, $3);
		}
		;

xedelete_expression	: Minus expression
		{
			$$ = buildv_edi(EDI_XEDELETE, 1, $2);
		}
		;

subentity_expression	: expression SubEntity lnpath
		{
			$$ = buildv_edi(EDI_SUBENTITY, 2, $1, $3);
			add_lineno($$, @$.first_line);
		}
		;

cond_expression	: expression Question expression Colon expression
		{
			$$ = buildv_edi(EDI_COND, 3, $1, $3, $5);
		}
		| expression Question expression Colon
		{
			$$ = buildv_edi(EDI_COND, 2, $1, $3);
		}
		;

case_expression	: expression Question case_body
		{
			$$ = buildv_edi(EDI_CASE, 2, $1, $3);
		}
		;

casevar_expression: CaseVar
		{
			$$ = buildv_edi(EDI_CASEVAR, 1, sr_create_int(ST_INT, $1));
		}
		;

case_body	: case_exec_expression
		{
			$$ = sr_create_dir();
			sr_append($$, $1);
		}
		| case_body Colon case_exec_expression
		{
			$$ = $1;
			sr_append($$, $3);
		}
		;

case_exec_expression	: expression CaseExec expression
		{
			$$ = sr_create_dir();
			sr_append($$, $1);
			sr_append($$, $3);
		}
		;

expressions	: expression Comma expression
		{
			$$ = sr_create(ST_EDEXPR);
			sr_append($$, $1);
			sr_append($$, $3);
		}
                | expressions Comma expression
		{
			sr_append($1, $3);
			$$ = $1;
		}
		;

expression	: func_expression
		| cast_expression
		| vector_expression
		| vector1_expression
		| while_expression
		| CNP_expression
		| CNP1_expression
		| cond_expression
		| case_expression
		| casevar_expression
		| set_expression
		| call_expression
		| xecreate_expression
		| xedelete_expression
		| subentity_expression
		| operators
		| LNP_expression
		| MemberLNP_expression
		| ArgVar_expression
		| UIArg_expression
		| UIVar_expression
		| CtxValVar_expression
		| ASRTV_expression
		| LeftRound Expression RightRound
		{
			$$ = $2;
		}
		;

cast_expression : CastSrt expression
		{
			sr_t	*sr_srt;
			srt_t	srt = ST_NULL;

			if (!checkmode && !sr_search_srt($1, &srt)) {
				*perrlineno = @1.first_line;
				ecm_asprintf(perrmsg, "no such entity type: <%s>", $1);
				ecm_free($1);
				sr_free($2);
				YYERROR;
			}
			ecm_free($1);
			sr_srt = sr_create_int(ST_SRT, srt);
			$$ = buildv_edi(EDI_CAST, 2, sr_srt, $2);
		}
		;

func_expression	: Identifier arglist
		{
			$$ = build_named_func_edi($1, $2, perrmsg);
			if ($$ == NULL) {
				*perrlineno = @1.first_line;
				YYERROR;
			}
		}
		;

operators	: binary_operators | unary_operators

binary_operators: expression Same expression
		{
			$$ = buildv_edi(EDI_SAME, 2, $1, $3);
		}
		| expression NotSame expression
		{
			$$ = buildv_edi(EDI_NOTSAME, 2, $1, $3);
		}
		| expression VectorPlus expression
		{
			$$ = buildv_edi(EDI_VECTORPLUS, 2, $1, $3);
		}
		| expression VectorMinus expression
		{
			$$ = buildv_edi(EDI_VECTORMINUS, 2, $1, $3);
		}
		| expression Plus expression
		{
			$$ = build_func_edi(PLUS_FID, 2, $1, $3);
		}
		| expression Minus expression
		{
			$$ = build_func_edi(MINUS_FID, 2, $1, $3);
		}
		| expression Equal expression
		{
			$$ = build_func_edi(EQUAL_FID, 2, $1, $3);
		}
		| expression NotEqual expression
		{
			$$ = build_func_edi(NOTEQUAL_FID, 2, $1, $3);
		}
		| expression RightAngle expression
		{
			$$ = build_func_edi(GREATERTHAN_FID, 2, $1, $3);
		}
		| expression LeftAngle expression
		{
			$$ = build_func_edi(LESSTHAN_FID, 2, $1, $3);
		}
		| expression GreaterEqual expression
		{
			$$ = build_func_edi(GREATEREQUAL_FID, 2, $1, $3);
		}
		| expression LessEqual expression
		{
			$$ = build_func_edi(LESSEQUAL_FID, 2, $1, $3);
		}
		| expression LogicalAnd expression
		{
			$$ = buildv_edi(EDI_LOGICALAND, 2, $1, $3);
		}
		| expression LogicalOr expression
		{
			$$ = buildv_edi(EDI_LOGICALOR, 2, $1, $3);
		}
		| expression BitwiseOr expression
		{
			$$ = buildv_edi(EDI_BITWISEOR, 2, $1, $3);
		}
		;

unary_operators	: Sharp expression
		{
			$$ = buildv_edi(EDI_CHILDCNT, 1, $2);
		}
		| LogicalNot expression
		{
			$$ = build_func_edi(LOGICALNOT_FID, 1, $2);
		}
		| Actuator expression
		{
			$$ = buildv_edi(EDI_ACTUATOR, 1, $2);
		}
		;

arglist		: LeftRound arguments RightRound
		{
			$$ = $2;
		}
		;

arguments	:
		{
			$$ = sr_create_dir();
		}
		| expressions
		| expression
		{
			$$ = sr_create_dir();
			sr_append($$, $1);
		}
		;

call_expression	: CallFunc expression Comma ASRT Comma arguments RightRound
		{
			$$ = build_CALL_edi($2, $6, $4);
		}
		| CallFunc expression Comma ASRT RightRound
		{
			$$ = build_CALL_edi($2, sr_create_dir(), $4);
 		}
		| CallOnewayFunc expression Comma arguments RightRound
		{
			$$ = build_CALL_edi($2, $4, NULL);
		}
		| CallOnewayFunc expression RightRound
 		{
			$$ = build_CALL_edi($2, sr_create_dir(), NULL);
		}
		;

LNP_expression	: lnpath
		{
			$$ = build_LNP_edi($1);
			add_lineno($$, @1.first_line);
		}
		;

MemberLNP_expression	: optional_ref Dollar optional_lnpath
		{
			$$ = build_memberLNP_edi(ECM_FALSE, $1, $2, $3);
			add_lineno($$, @1.first_line);
		}
		| Equal optional_ref Dollar optional_lnpath
		{
			$$ = build_memberLNP_edi(ECM_TRUE, $2, $3, $4);
			add_lineno($$, @1.first_line);
		}
		;

ArgVar_expression: optional_ref ArgVar optional_lnpath
		{
			$$ = build_ArgVar_edi($1, $2, $3);
		}
		;

UIArg_expression	: UIArgVar
		{
			$$ = build_UIArg_edi($1);
		}
		;

UIVar_expression: UIVar
		{
			$$ = build_UIVar_edi($1, ECM_FALSE);
		}
		| UIVars
		{
			$$ = build_UIVar_edi($1, ECM_TRUE);
		}
		;

CtxValVar_expression: CtxValVar
		{
			$$ = build_CtxValVar_edi($1);
		}
		;

optional_lnpath	:
		{
			$$ = NULL;
		}
		| lnpath
		{
			$$ = $1;
		}
		;

lnpath		: LNP
		{
			$$ = build_lnpath(ECM_FALSE, $1);
			if ($$ == NULL) {
				*perrlineno = @1.first_line;
				ecm_asprintf(perrmsg, "Invalid LNP: %s", $1);
				ecm_free($1);
				YYERROR;
			}
			ecm_free($1);
		}
		| PassiveLNPath
		{
			$$ = build_lnpath(ECM_TRUE, $1);
			if ($$ == NULL) {
				*perrlineno = @1.first_line;
				ecm_asprintf(perrmsg, "Invalid passive LNP: %s", $1);
				ecm_free($1);
				YYERROR;
			}
			ecm_free($1);
		}
		;

LNP		: literal | LNPath;

literal		: String | alpha_numeric;

alpha_numeric	: Identifier;

ASRTV		: asrt ASRV
		{
			if (checkmode) {
				sr_free($1);
				sr_free($2);
				$$ = NULL;
			}
			else {
				char	*errmsg;
				$$ = edl_create_sr_from_ASR(NULL, $1, $2, &errmsg);
				if ($$ == NULL) {
					*perrlineno = @1.first_line;
					*perrmsg = errmsg;
					YYERROR;
				}
			}
		}
		;

ASR		: literal asrt optional_ASRV
		{
			if (checkmode) {
				ecm_free($1);
				sr_free($2);
				sr_free($3);
				$$ = NULL;
			}
			else {
				char	*errmsg;
				$$ = edl_create_sr_from_ASR($1, $2, $3, &errmsg);
				if ($$ == NULL) {
					*perrlineno = @1.first_line;
					*perrmsg = errmsg;
					YYERROR;
				}
			}
		}
		;

ASR_stmt	: ASR Semicolon;

asrt		: ASRT
		{
			if (checkmode) {
				$$ = NULL;
			}
			else {
				$$ = edl_create_sr_from_ASRT($1);
				if ($$ == NULL) {
					*perrlineno = @1.first_line;
					ecm_asprintf(perrmsg, "no such entity type: <%s>", $1);
					ecm_free($1);
					YYERROR;
				}
			}
			ecm_free($1);
		}
		;

optional_ref	:
		{
			$$ = ECM_FALSE;
		}
		| Ampersand
		{
			$$ = ECM_TRUE;
		}
		;

ASRV		: literal
		{
			$$ = sr_set_val(sr_create(ST_STR), sr_val_str($1));
		}
		| ASR_block
		;

optional_ASRV	:
		{
			$$ = NULL;
		}
		| ASRV
		;

ASR_block	: LeftBrace ASR_stmts RightBrace
		{
			$$ = $2;
		}
		;

ASR_stmts	:
		{
			$$ = sr_create_dir();
		}
		| ASR_stmts ASR_stmt
		{
			sr_append($1, $2);
			$$ = $1;
		}
		;

%%
